//   Copyright 2012,2013 Vaughn Vernon
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package com.saasovation.agilepm.application;

import com.saasovation.common.port.adapter.notification.RabbitMQNotificationPublisher;
import org.iq80.leveldb.DB;

import com.saasovation.agilepm.application.notification.NotificationApplicationService;
import com.saasovation.agilepm.port.adapter.persistence.LevelDBDatabasePath;
import com.saasovation.common.domain.model.DomainEvent;
import com.saasovation.common.domain.model.DomainEventPublisher;
import com.saasovation.common.domain.model.DomainEventSubscriber;
import com.saasovation.common.event.EventStore;
import com.saasovation.common.notification.NotificationPublisher;
import com.saasovation.common.notification.PublishedNotificationTrackerStore;
import com.saasovation.common.port.adapter.messaging.Exchanges;
import com.saasovation.common.port.adapter.notification.SlothMQNotificationPublisher;
import com.saasovation.common.port.adapter.persistence.leveldb.LevelDBEventStore;
import com.saasovation.common.port.adapter.persistence.leveldb.LevelDBProvider;
import com.saasovation.common.port.adapter.persistence.leveldb.LevelDBPublishedNotificationTrackerStore;
import com.saasovation.common.port.adapter.persistence.leveldb.LevelDBUnitOfWork;

/**
 * 应用服务生命周期
 * @author learn
 */
public class ApplicationServiceLifeCycle {

    /**
     * LevelDB
     */
    private static final DB database;
    /**
     * 事件存储
     */
    private static final EventStore eventStore;
    /**
     * 通知应用服务
     */
    private static NotificationApplicationService notificationApplicationService;
    /**
     * 通知发布者
     */
    private static NotificationPublisher notificationPublisher;
    /**
     * 计时器（通知发布者）
     */
    private static NotificationPublisherTimer timer;
    /**
     * 已发布的通知追踪存储
     */
    private static PublishedNotificationTrackerStore publishedNotificationTrackerStore;

    /**
     * 静态块儿
     */
    static {
        // 设置数据库
        database =
                LevelDBProvider
                    .instance()
                    .databaseFrom(LevelDBDatabasePath.agilePMPath());

        // 设置事件存储
        eventStore = new LevelDBEventStore(LevelDBDatabasePath.agilePMPath());
        // 设置计时器
        timer = new NotificationPublisherTimer();

        // 设置已发布通知追踪存储
        publishedNotificationTrackerStore =
                new LevelDBPublishedNotificationTrackerStore(
                        LevelDBDatabasePath.agilePMPath(),
                        "saasovation.agilepm");
        // 设置通知发布者
        notificationPublisher =
                new RabbitMQNotificationPublisher(
                        eventStore,
                        publishedNotificationTrackerStore,
                        Exchanges.AGILEPM_EXCHANGE_NAME);
        // 设置通知发布者
//        notificationPublisher =
//                new SlothMQNotificationPublisher(
//                        eventStore,
//                        publishedNotificationTrackerStore,
//                        Exchanges.AGILEPM_EXCHANGE_NAME);

        // 设置通知应用服务
        notificationApplicationService = new NotificationApplicationService(notificationPublisher);
        // 计时器启动
        timer.start();
    }

    /**
     * 启动（应用服务生命周期）
     */
    public static void begin() {
        ApplicationServiceLifeCycle.begin(true);
    }

    /**
     * 启动（应用服务）
     * @param isListening       是否监听
     */
    public static void begin(boolean isListening) {
        // 如果是监听，则监听
        if (isListening) {
            ApplicationServiceLifeCycle.listen();
        }
        // 数据库也启动
        LevelDBUnitOfWork.start(database);
    }

    /**
     * 失败处理
     */
    public static void fail() {
        // 数据库当前线程进行回滚
        LevelDBUnitOfWork.current().rollback();
    }

    /**
     * 因异常失败
     * @param anException       异常
     */
    public static void fail(RuntimeException anException) {
        // 首先，做失败处理
        ApplicationServiceLifeCycle.fail();
        // 抛出异常
        throw anException;
    }

    /**
     * 抛出异常失败
     * @param aThrowable        可抛出异常
     * @throws Throwable        抛出异常
     */
    public static void fail(Throwable aThrowable) throws Throwable {
        // 首先，做失败处理
        ApplicationServiceLifeCycle.fail();
        // 抛出异常
        throw aThrowable;
    }

    /**
     * 成功处理
     */
    public static void success() {
        // 数据库当前线程进行提交
        LevelDBUnitOfWork.current().commit();
    }

    /**
     * 监听
     */
    private static void listen() {
        // 领域事件发布者进行重置
        DomainEventPublisher.instance().reset();
        // 领域事件发布者进行订阅
        DomainEventPublisher
            .instance()
            .subscribe(new DomainEventSubscriber<DomainEvent>() {

                /**
                 * 处理事件
                 * @param aDomainEvent 领域事件泛型类型
                 */
                @Override
				public void handleEvent(DomainEvent aDomainEvent) {
                    // 在事件存储追加领域事件
                    eventStore.append(aDomainEvent);
                }

                /**
                 * 订阅事件类型
                 * @return             返回领域事件的类
                 */
                @Override
                public Class<DomainEvent> subscribedToEventType() {
                    return DomainEvent.class; // all domain events
                }
            });
    }

    // TODO: need to monitor this...

    /**
     * 通知发布者计时器
     */
    private static class NotificationPublisherTimer extends Thread {
        /**
         * 默认构造器
         */
        public NotificationPublisherTimer() {
            // 调用父类构造器
            super();
        }

        /**
         * 运行（线程）
         */
        @Override
        public void run() {
            // 死循环
            while (true) {
                try {
                    // 使用通知应用服务，发布通知
                    notificationApplicationService.publishNotifications();
                } catch (Exception e) {
                    System.out.println("Problem publishing notifications from ApplicationServiceLifeCycle.");
                }
                // 处理一次后，再次停留100毫秒
                try {
                    Thread.sleep(100L);
                } catch (Exception e) {
                    // ignore
                }
            }
        }
    }
}
